#include "stdafx.h"
#include <string>
#include <iostream>
#include <assert.h>
#include <boost/asio.hpp>
#include "socket.h"
using namespace std;

boost::asio::io_service io_service;

class CTcpSocket : public ISocket
{
public:
	CTcpSocket() : m_socket(io_service)
	{
		m_local_ip=m_remote_ip=m_bytes_transferred=0;
		m_local_port=m_remote_port=0;
	}

	virtual ~CTcpSocket()
	{
		close();
	}

	virtual int connect(uint32 ip, uint16 port)
	{
		m_remote_ip=ip;
		m_remote_port=port;

		boost::asio::ip::address_v4 addr(ip);
		boost::asio::ip::tcp::endpoint ep(addr, port);
		boost::system::error_code e;
		m_socket.connect(ep, e);
		if (e) return e.value();

		ep=m_socket.local_endpoint(e);
		m_local_ip=ep.address().to_v4().to_ulong();
		m_local_port=ep.port();
		return 0;
	}

	virtual void close()
	{
		m_socket.close();
	}

	virtual void timeout(int ms)
	{
	}

	virtual int	 timeout()
	{
		return 0x7fffffff;
	}

	virtual Handle		handle()				{assert("N/A");return NULL;}
	virtual uint32		remote_ip()				{return m_remote_ip;}
	virtual uint16		remote_port()			{return m_remote_port;}
	virtual uint32		local_ip()				{return m_local_ip;}
	virtual uint16		local_port()			{return m_local_port;}
	virtual size_t		bytes_transferred()		{return m_bytes_transferred;}

	virtual int bind(uint16 port)
	{
		m_local_ip=0;
		m_local_port=port;
		boost::asio::ip::address_v4 addr(0);
		boost::asio::ip::tcp::endpoint ep(addr, port);
		boost::system::error_code e;
		m_socket.bind(ep, e);
		return e.value();
	}

	virtual int send(void* buf, size_t size)
	{
		boost::system::error_code e;
		boost::asio::write(m_socket, 
			boost::asio::buffer(&size, sizeof(size)), 
			boost::asio::transfer_all(), e);
		boost::asio::write(m_socket, 
			boost::asio::buffer(buf, size), 
			boost::asio::transfer_all(), e);
		return e ? e.value() : size;
	}

	virtual int recv(void* buf, size_t size)
	{
		size_t s;
		boost::system::error_code e;
		boost::asio::read(m_socket,
			boost::asio::buffer(&s, sizeof(s)),
			boost::asio::transfer_all(), e);
		assert(s<=size);

		boost::asio::read(m_socket,
			boost::asio::buffer(buf, s),
			boost::asio::transfer_all(), e);
		return e ? e.value() : s;
	}

	virtual const char*	error_string(int code)
	{
		//todo: construct error object from code,
		//and get the message
		return m_error_string.c_str();
	}

//protected:
	boost::asio::ip::tcp::socket m_socket;
	uint32 m_local_ip, m_remote_ip, m_bytes_transferred;
	uint16 m_local_port, m_remote_port;
	std::string m_error_string;
};

ISocket* new_tcp_bsocket()		{return new CTcpSocket;}
